package edu.ccut.saturn.operation.database;

import java.io.BufferedInputStream;
import java.io.BufferedReader;
import java.sql.CallableStatement;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.Types;
import java.util.ArrayList;
import java.util.List;

import edu.ccut.saturn.component.IDataInfo;
import edu.ccut.saturn.component.IDataItem;
import edu.ccut.saturn.component.ILogicContext;
import edu.ccut.saturn.component.IParameterContext;
import edu.ccut.saturn.component.SaturnComponentException;
import edu.ccut.saturn.component.SaturnData;
import edu.ccut.saturn.component.SaturnDataManager;

public class ExecuteProcedure {
    //( 5  in  ,  30 30  in   ,    varchar   out   )
	public void execute(IParameterContext parameterContext) throws Exception {
		String procName = ""; //$NON-NLS-1$
		procName = (String) parameterContext.getParameterValue(0);
		String paramExp = (String) parameterContext.getParameterValue(1);
		ILogicContext logicContext = (ILogicContext)parameterContext.getParameterValue(3);
		if(paramExp!=null){
			paramExp =  parseCondtion(paramExp,logicContext);
		}
		List<Object> list = null;
		if (!(paramExp == null || paramExp.equals("''") || paramExp.equals(""))) { //$NON-NLS-1$ //$NON-NLS-2$
			list = parseExpression( paramExp);
		}
		StringBuffer callExp = new StringBuffer("{call "); //$NON-NLS-1$
		Connection conn = parameterContext.getConnection();
		CallableStatement cstat = null;
		ResultSet rs = null;
		List<Object> returnList = new ArrayList<Object>();
		callExp.append(procName);
		int paramIndex = 1;
		try {
			if (list == null || list.size() == 0) {
				callExp.append("()}"); //$NON-NLS-1$
				cstat = conn.prepareCall(callExp.toString());
				cstat.execute();
			} else {
				callExp.append("("); //$NON-NLS-1$
				for (int i = 0; i < list.size() / 2; i++) {
					callExp.append("?,"); //$NON-NLS-1$
				}
				callExp.deleteCharAt(callExp.length() - 1);
				callExp.append(")}"); //$NON-NLS-1$
				cstat = conn.prepareCall(callExp.toString());
				for (int i = 0; i < list.size(); i++) {
					String type = (String) list.get(i);
					if ("in".equals(type)) { //$NON-NLS-1$
						Object temp = list.get(++i);
						cstat.setObject(paramIndex++,temp.equals("null")?"":temp ); //$NON-NLS-1$
					} else if ("out".equals(type)) { //$NON-NLS-1$
						String outType = (String) list.get(++i);
						if ("varchar".equals(outType)) { //$NON-NLS-1$
							cstat.registerOutParameter(paramIndex++,
									Types.VARCHAR);
						} else {
							cstat.registerOutParameter(paramIndex++,
									oracle.jdbc.OracleTypes.CURSOR);
						} 
					} else if("inout".equals(type)){
						Object temp = list.get(++i);
						cstat.setObject(paramIndex,temp.equals("null")?"":temp ); //$NON-NLS-1$
						cstat.registerOutParameter(paramIndex++,
								Types.VARCHAR);
					}
				}
				cstat.execute();
				paramIndex = 1;
//				for (int i = 0; i < list.size(); i++) {
//					debug("取出参数111");
//					String type = (String) list.get(i);
//					debug("type1111==="+type);
//				}
				for (int i = 0; i < list.size(); i++) {
					String type = (String) list.get(i);
					if ("out".equals(type)) { //$NON-NLS-1$
						String outType = (String) list.get(++i);
						if ("varchar".equals(outType)) { //$NON-NLS-1$
							returnList.add(cstat.getObject(paramIndex++));
						} else {
							SaturnData saturnData = getSaturnDataByFullKey(outType);
							rs = (ResultSet) cstat.getObject(paramIndex++);
							List<SaturnData> saturnList = setListValueFromRs(saturnData, rs);
							returnList.add(saturnList);
						}
					}else if("inout".equals(type)){
						++i;
						returnList.add(cstat.getObject(paramIndex++));
					} else {
						i++;
						paramIndex++;
					}
				}
			}
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			if (cstat != null){
				try {
					cstat.close();
				} catch (Exception e) {
					e.printStackTrace();
					throw new SaturnComponentException("statment close exception");  //$NON-NLS-1$
				}
			}
			if(rs !=null){
				try {
					rs.close();
				} catch (Exception e) {
					e.printStackTrace();
					throw new SaturnComponentException("statment close exception");  //$NON-NLS-1$
				}
			}
		}
		if(returnList.size()==0){
			returnList = null;
		}
        parameterContext.setParameter(2, returnList);
	}

	private List<Object> parseExpression(
			String paramExp) {
		List<Object> list = new ArrayList<Object>();
		String[] itemStr = paramExp.split(","); //$NON-NLS-1$
		for (String item : itemStr) {
			item=item.trim();
			if(item.endsWith(" in")){
				list.add("in");
				list.add(item.substring(0, item.length()-3).trim());
			}else if(item.endsWith(" out")){
				list.add("out");
				list.add(item.substring(0, item.length()-4).trim());
			}else if(item.endsWith(" inout")){
				list.add("inout");
				list.add(item.substring(0, item.length()-6).trim());
			}
		}
		return list;
	}
	private List<SaturnData> setListValueFromRs(SaturnData data, ResultSet rs)
			throws Exception {
		IDataInfo dataInfo = data.getSaturnDataInfo();
		// Object[] keys = dataInfo.keySet().toArray();
		List<SaturnData> list = new ArrayList<SaturnData>();
		String packagename = dataInfo.getSpace();
		String getClassName = dataInfo.getType();
		// 得到查询结果的列数和列名称
		int columnCount = rs.getMetaData().getColumnCount();
		String[] columnNames = new String[columnCount];
		for (int i = 0; i < columnCount; i++) {
			columnNames[i] = rs.getMetaData().getColumnName(i+1);
		}
		//存放真正需要处理的key
		ArrayList<String> usedKeyArr = new ArrayList<String>();
		for(String key:dataInfo.keySet()){
			for(String column :columnNames){
				if(column.equalsIgnoreCase(key))
					usedKeyArr.add(key);
			}
		}
		SaturnData saturnData = null;
		while (rs.next()) {
			saturnData = SaturnDataManager.getInstance().createData(packagename,
					getClassName);
			for (int i = 0; i < usedKeyArr.size(); i++) {
				// 名称要变换为区分大小写的格式
				String usedKey =usedKeyArr.get(i);
				IDataItem dataItem = dataInfo.getDataItem(usedKey);
				if (dataItem != null) {
					String type = dataItem.getType();
					String javatype = dataItem.getJavaType();
					if ("BLOB".equals(type)) { //$NON-NLS-1$
						BufferedInputStream is = null;
						byte[] value = null;
						try {
							oracle.sql.BLOB blob = (oracle.sql.BLOB) rs
									.getBlob(usedKey);
							if (blob != null) {
								is = new BufferedInputStream(blob
										.getBinaryStream());
								value = new byte[(int) blob.length()];
								is.read(value);
							}
						} catch (Exception e) {
							e.printStackTrace();
							throw new SaturnComponentException("BLOB Exception"); //$NON-NLS-1$
						} finally {
							try {
								if (is != null)
									is.close();
							} catch (Exception e) {
								e.printStackTrace();
								throw new SaturnComponentException(
										"InputStream close exception"); //$NON-NLS-1$
							}
						}
						saturnData.set(usedKey, value);
					} else if ("CLOB".equals(type)) { //$NON-NLS-1$
						BufferedReader reader = null;
						char[] charValue = null;
						String value = null;
						try {
							oracle.sql.CLOB clob = (oracle.sql.CLOB) rs
									.getClob(usedKey);
							if (clob != null) {
								reader = new BufferedReader(clob
										.getCharacterStream());
								charValue = new char[(int) clob.length()];
								reader.read(charValue);
								value = new String(charValue);
							}
						} catch (Exception e) {
							e.printStackTrace();
							throw new SaturnComponentException("CLOB exception"); //$NON-NLS-1$
						} finally {
							try {
								if (reader != null)
									reader.close();
							} catch (Exception e) {
								e.printStackTrace();
								throw new SaturnComponentException(
										"reader close exception"); //$NON-NLS-1$
							}
						}
						saturnData.set(usedKey, value);
					} else {
						setSaturnData(saturnData, usedKey, rs, javatype);
					}
				}
			}
			list.add(saturnData);
		}
		if (list.size() == 0) {
			return null;
		}
		return list;
	}

	private void setSaturnData(SaturnData saturnData, String key, ResultSet rs,
			String javaType) throws Exception {
		if ("java.lang.Integer".equals(javaType)) { //$NON-NLS-1$
			saturnData.set(key, rs.getInt(key));// TODO BigDecimal Integer 转换问题
		} else if ("java.lang.Double".equals(javaType)) { //$NON-NLS-1$
			saturnData.set(key, rs.getDouble(key));// TODO BigDecimal Double
													// 转换问题
		} else if ("java.lang.Float".equals(javaType)) { //$NON-NLS-1$
			saturnData.set(key, rs.getFloat(key));// TODO BigDecimal Float
													// 转换问题
		} else if ("java.math.BigDecimal".equals(javaType)) { //$NON-NLS-1$
			saturnData.set(key, rs.getBigDecimal(key));
		} else if ("java.lang.Short".equals(javaType)) { //$NON-NLS-1$
			saturnData.set(key, rs.getShort(key));
		} else if ("java.lang.Long".equals(javaType)) { //$NON-NLS-1$
			saturnData.set(key, rs.getLong(key));
		} else if ("java.sql.Date".equals(javaType)) { //$NON-NLS-1$
			saturnData.set(key, rs.getDate(key));
		} else if ("java.lang.String".equals(javaType)) { //$NON-NLS-1$
			saturnData.setString(key, rs.getString(key));
		} else if ("byte".equals(javaType)) { //$NON-NLS-1$
			saturnData.set(key, rs.getByte(key));
		} else {
			saturnData.set(key, rs.getObject(key));
		}
	}

	private SaturnData getSaturnDataByFullKey(String fullKey)
			throws SaturnComponentException {
		SaturnData saturnData = SaturnDataManager.getInstance()
				.createData(fullKey);
		if (saturnData == null) {
			// TODO 抛出异常
			throw new SaturnComponentException("Saturn data parameter is null."); //$NON-NLS-1$
		}
		return saturnData;
	}
	private String parseCondtion(String condition, ILogicContext context) {
		StringBuffer newCondition =new StringBuffer().append(""); //$NON-NLS-1$
		char[] charArray = condition.toCharArray();
	    StringBuffer oldexp=new StringBuffer();//${}表达式
	    boolean isGetExp = false; //是否在拼${}表达式
	    boolean is$ = false;
	    for(char currItem:charArray){
	    	if(is$){
	    		if(currItem=='{'){
	    			//开始拼表达式
	    			isGetExp=true;
	    			oldexp.append("${"); //$NON-NLS-1$
	    		}else{
	    			//只是普通$符号
	    			newCondition.append("$").append(currItem); //$NON-NLS-1$
	    		}
	    		is$=false;
	    	}else{
	    		if(isGetExp){
	    			if(currItem!='}'){
	    				oldexp.append(currItem);
	    			}else{
	    				oldexp.append(currItem);
	    				isGetExp=false;
	    				//解析
	    				Object value = context.getValue(oldexp.toString());
	    				if(value!=null&&!value.equals("")){ //$NON-NLS-1$
	    					newCondition.append(value);
	    				}else{
	    					//如果没有值，则附值为“null”,标示为空
	    					newCondition.append("null"); //$NON-NLS-1$
	    				}
	    				oldexp.delete(0, oldexp.length());
	    			}
		    	}else{
		    		if(currItem!='$'){
		    			newCondition.append(currItem);
		    		}else{
		    			//判断是否开始拼表达式
		    			is$=true;
		    		}
		    	}	
	    	}
	    }
		return newCondition.toString();
	}

}
